/*
 * Ingenic SU ADC solution.
 *
 * Copyright (C) 2015 Ingenic Semiconductor Co.,Ltd
 * Author: Tiger <bohu.liang@ingenic.com>
 */

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include <imp/imp_log.h>
#include <imp/imp_utils.h>
#include <constraints.h>

#include <sysutils/su_adc.h>


/* define ioctl command. they are fixed. don't modify! */
#define ADC_ENABLE 		0
#define ADC_DISABLE 	1
#define ADC_SET_VREF 	2

#define TAG		"ADC"
#define ADC_NUM		1
#define ADC_PATH	"/dev/jz_adc_aux_"
#define ADC_PATH_LEN	32
#define STD_VAL_VOLTAGE 1800 /* The unit is mv. T10/T20 VREF=3300; T30/T21/T31 VREF=1800 */

typedef struct {
	int fd[ADC_NUM];
}SUADC;

static SUADC *gadc = NULL;

static SUADC *getadc(void )
{
	return gadc;
}

static void setadc(SUADC *adc)
{
	gadc = adc;
}

int SU_ADC_Init(void )
{
	int fd;
	int i, j, ret;
	SUADC *adc;
	char path[ADC_PATH_LEN];

	adc = calloc(1, sizeof(SUADC));
	if(adc == NULL) {
		IMP_LOG_ERR(TAG, "calloc error !\n");
		ret = -1;
		goto error_calloc;
	}

	for(i = 0; i < ADC_NUM; i++) {
		sprintf(path, "%s%d", ADC_PATH, i);
		fd = open(path, O_RDONLY);
		if(fd < 0) {
			IMP_LOG_ERR(TAG, "open error !\n");
			ret = -1;
			goto error_open;
		}

		adc->fd[i] = fd;
	}

	setadc(adc);

	return 0;

error_open:
	for(j = 0; j < i; i++)
		close(adc->fd[j]);
error_calloc:
	return ret;
}

int SU_ADC_Exit(void )
{
	int i;
	SUADC *adc = getadc();

	if(adc == NULL)
		return 0;

	for(i = 0; i < ADC_NUM; i++)
		close(adc->fd[i]);

	return 0;
}

int SU_ADC_EnableChn(uint32_t chn_num)
{
	SUADC *adc = getadc();
    int vol_n;

	if(adc == NULL) {
		IMP_LOG_ERR(TAG, "adc is NULL !\n");
		return -1;
	}

	if((chn_num >= ADC_NUM) || (chn_num < 0)) {
		IMP_LOG_ERR(TAG, "chn_num error !\n");
		return -1;
	}

	/* set reference voltage */
	vol_n = STD_VAL_VOLTAGE;
	if(ioctl(adc->fd[chn_num], ADC_SET_VREF, &vol_n) < 0) {
		IMP_LOG_ERR(TAG, "Failed to set reference voltage!\n");
	    return -1;
    }

	if (ioctl(adc->fd[chn_num], ADC_ENABLE) < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):chn_num=%d, ioctl(%d, 0):%s!\n", __func__, __LINE__, chn_num, adc->fd[chn_num], strerror(errno));
		return -1;
	}

	return 0;
}

int SU_ADC_DisableChn(uint32_t chn_num)
{
	SUADC *adc = getadc();

	if(adc == NULL) {
		IMP_LOG_ERR(TAG, "adc is NULL !\n");
		return -1;
	}

	if((chn_num >= ADC_NUM) || (chn_num < 0)) {
		IMP_LOG_ERR(TAG, "chn_num error !\n");
		return -1;
	}

	if (ioctl(adc->fd[chn_num], ADC_DISABLE) < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):chn_num=%d, ioctl(%d, 1):%s!\n", __func__, __LINE__, chn_num, adc->fd[chn_num], strerror(errno));
		return -1;
	}

	return 0;
}

int SU_ADC_GetChnValue(uint32_t chn_num, int *value)
{
	SUADC *adc = getadc();

	if(adc == NULL) {
		IMP_LOG_ERR(TAG, "adc is NULL !\n");
		return -1;
	}

	if((chn_num >= ADC_NUM) || (chn_num < 0)) {
		IMP_LOG_ERR(TAG, "chn_num error !\n");
		return -1;
	}

	if(value == NULL) {
		IMP_LOG_ERR(TAG, "value error !\n");
		return -1;
	}

	if (read(adc->fd[chn_num], value, sizeof(int)) < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):chn_num=%d, read(%d) failed:%s!\n", __func__, __LINE__, chn_num, adc->fd[chn_num], strerror(errno));
		return -1;
	}

	return 0;
}
